package com.jwcao.common.cast.context;

import com.jwcao.common.cast.annotation.CastObject;
import com.jwcao.common.cast.annotation.factory.CastObjectFactory;
import com.jwcao.common.cast.operation.CastFlow;
import com.jwcao.common.cast.operation.CastFlowFactory;
import com.jwcao.common.scan.ClassScanner;
import com.jwcao.common.scan.object.ScanClass;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import org.apache.commons.lang3.ArrayUtils;

/**
 * name: CastContextFactory description: TODO
 *
 * @author caojingwei date: 2023/3/23 20:08
 */
public class CastFlowContextFactory {

    private static final AtomicInteger INIT_COUNT = new AtomicInteger(0);

    private static final Predicate<Class> CLASS_PREDICATE = (Class clazz) -> {
        CastObject[] casts = (CastObject[]) clazz.getAnnotationsByType(CastObject.class);
        return ArrayUtils.isNotEmpty(casts);
    };

    public static void init(String[] packageName) throws IOException, ClassNotFoundException {
        boolean hasInit = INIT_COUNT.compareAndSet(0, 1);
        if (!hasInit) {
            // 不允许重复初始化
            return;
        }
        ClassScanner classScanner = ClassScanner.builder()
            .basePackages(packageName)
            .classPredicate(CLASS_PREDICATE)
            .build();
        List<ScanClass> classList = classScanner.doScanAllScanClasses();
        classList.forEach(CastFlowContextFactory::registerCast);
        CastContext.flush();
    }

    public static void registerCast(ScanClass sourceClass) {
        ScanClass sourceScanClass = CastContext.registerClass(sourceClass);
        List<CastObject> castObjectList = CastObjectFactory.get(sourceClass.getClazz());
        castObjectList.stream().forEach(c -> registerCast(sourceScanClass, c));
    }

    public static void registerCast(ScanClass sourceClass, CastObject castObject) {
        ScanClass targetClass = CastContext.registerClass(CastObjectFactory.getClass(castObject));
        CastFlow castFlow = CastFlowFactory.get(castObject.id(), sourceClass, targetClass);
        CastContext.registerCast(castFlow);
    }

}
